package org.simplextensions.registry;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.simplextensions.di.DependencyInjector;
import org.simplextensions.di.annotations.Registry;

import com.sun.corba.se.spi.legacy.connection.GetEndPointInfoAgainException;

/**
 * 
 * @author Tomasz Krzyzak, <a
 *         href="mailto:tomasz.krzyzak@gmail.com">tomasz.krzyzak@gmail.com</a>
 * @since 2009-07-29 21:58:02
 * 
 */
@org.simplextensions.annotations.ExtensionPoint(id = IService.EP_ID, extensionClass = { IService.class, IServiceLocator.class })
public class ServiceRegistry implements IServiceRegistry {

	private static final Log log = LogFactory.getLog(ServiceRegistry.class);

	private Map<Class<?>, IService> servicesMap = new HashMap<Class<?>, IService>();
	private Map<Class<?>, Extension> extensionsMap = new HashMap<Class<?>, Extension>();

	private Map<IServiceLocator, Extension> serviceLocators = new HashMap<IServiceLocator, Extension>();

	@Registry
	private IExtensionRegistry extensionRegistry;

	public ServiceRegistry() {
		log.info("ServiceRegistry created...");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.simplextensions.core.IIServiceRegistry#getService(java.lang.Class)
	 */
	@SuppressWarnings("unchecked")
	public <T> T getService(Class<T> serviceInterface) {
		if (!isStarted())
			return null;

		IService service = servicesMap.get(serviceInterface);
		if (service == null) {
			Extension extension = extensionsMap.get(serviceInterface);
			if (extension != null) {
				service = (IService) extension.getExecutable();
				log.info("Initializing service: " + serviceInterface.getCanonicalName());
				// service.init(this);
				servicesMap.put((Class<? extends IService>) serviceInterface, service);
			}
		}
		if (service != null) {
			// start service
			if (!service.isStarted()) {
				log.info("Starting service: " + serviceInterface.getCanonicalName());
				service.start();
			}

			// check if starter
			if (service.isStarted())
				return (T) service;
			else
				return null;
		} else {
			for (Entry<IServiceLocator, Extension> entry : serviceLocators.entrySet()) {
				T result = entry.getKey().getService(this, serviceInterface);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.simplextensions.core.IIServiceRegistry#registerService(java.lang.
	 * Class, K)
	 */
	public synchronized <T, K extends IService> void registerService(Class<T> serviceInterface, K service) {
		if (log.isInfoEnabled())
			log.info("Registering service: " + service.getClass().getCanonicalName() + " with interface: "
					+ serviceInterface.getCanonicalName());
		DependencyInjector.resolveDependency(getExtensionRegistry(), service);
		this.servicesMap.put(serviceInterface, service);
		// service.init(this);
		if (started) {
			service.start();
		}
	}

	public void registerServiceLocator(IServiceLocator serviceLocator) {
		this.serviceLocators.put(serviceLocator, null);
	}

	boolean started = false;

	public boolean isStarted() {
		return started;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.simplextensions.registry.IServiceRegistry#start()
	 */
	public synchronized void start() {
		if (started) {
			throw new RuntimeException("already started");
		}
		log.info("Starting Service Registry");
		started = true;
		for (Entry<IServiceLocator, Extension> entry : serviceLocators.entrySet()) {
			try {
				entry.getKey().start(this);
			} catch (Exception e) {
				log.error("", e);
			}
		}
		for (Class<?> service : servicesMap.keySet()) {
			getService(service);
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.simplextensions.registry.IServiceRegistry#stop()
	 */
	public void stop() {
		if (!started) {
			throw new RuntimeException("not yet started");
		}
		log.info("Stopping Service Registry");

		for (Entry<IServiceLocator, Extension> entry : serviceLocators.entrySet()) {
			try {
				entry.getKey().stop(this);
			} catch (Exception e) {
				log.error("", e);
			}

		}

		for (IService service : servicesMap.values()) {
			try {
				service.stop();
			} catch (Exception e) {
				log.error("", e);
			}
		}
	}

	@SuppressWarnings("unchecked")
	public void initialize(ExtensionPoint extensionPoint) {
		Collection<Extension> extensions = extensionPoint.getActiveExtensions();
		for (Extension e : extensions) {
			Class<?> clazz = e.getClazz();
			if (IService.class.isAssignableFrom(clazz)) {
				PropertyValue propertyValue = e.getPropertyValue(IService.SERVICE_INTERFACE);
				if (propertyValue == null) {
					log.warn(e.getId() + " does not define parameter: " + IService.SERVICE_INTERFACE);
					continue;
				}
				Class<?> classValue = propertyValue.getClassValue();
				if (classValue != null && IService.class.isAssignableFrom(e.getClazz())) {
					log.debug("registering service: " + e.getId() + " binded to iface: " + classValue.getCanonicalName());
					extensionsMap.put((Class<? extends IService>) classValue, e);
				}
			} else if (IServiceLocator.class.isAssignableFrom(clazz)) {
				serviceLocators.put((IServiceLocator) e.getExecutable(), e);
			}
		}
	}

	public IExtensionRegistry getExtensionRegistry() {
		return extensionRegistry;
	}

}
